/*
 *  Copyright 2006 Noah Sloan <iamnoah AT gmail D0T com>, U.S.A.  
 *
 *  These libraries are free to use under the terms of the 
 *  LGPL <http://www.gnu.org/licenses/lgpl.html> [i.e. any terms you like :)]
 *  No guarantee is made to the usefulness, saftey, etc.  
 *  i.e. Use at your own risk. 
 *  The author is in no way liable for anything that might happen as a result of using 
 *  this code, yada yada yada.  Have fun.
 */
package org.nutils.streams;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * A DelimitedInputStream wraps another InputStream and allows reading from that
 * InputStream up to and including a given delimeter (a byte sequence).
 * <p>
 * It is useful for wrapping InputStreams that you want to pass to a Scanner or
 * other buffering reader but that will need to be accessed directly later. i.e.
 * you need a Scanner but aren't willing to give up all the data.
 * <p>
 * Note that if you know exactly how many bytes you want to give to the Scanner
 * (or whatever) you should use a {@link PartialInputStream} as it implements
 * all 3 read methods and will generally be more efficient.
 * <p>
 * Note that this class will <em>never</em> buffer ahead, so it may be
 * desirable to wrap a stream in a {@link BufferedInputStream} before handing it
 * to this class.
 * <p>
 * Regex support will be implemented in another class in a future release,
 * however, it is possible to delimit on multiple tokens by layering multiple
 * <code>DelimitedInputStream</code>s (since a caught delimeter in one layer
 * will result in an EOS all the way up to the top layer)
 * 
 * @author Noah Sloan
 * 
 */
public class DelimitedInputStream extends InputStream {

	private byte[] delimeter; // the delimeter to look for

	private byte[] seen; // the last delimeter.length bytes seen

	private int index; // front of seen

	private InputStream in; // stream to read bytes from

	public DelimitedInputStream(InputStream in, String delimeter) {
		this(in, delimeter.getBytes());
	}

	/**
	 * 
	 * @param in
	 * @param delimeter
	 *            the delimeter to halt after
	 */
	public DelimitedInputStream(InputStream in, byte[] delimeter) {
		this.in = in;
		this.delimeter = delimeter;
		this.seen = new byte[delimeter.length];
		this.index = 0;
	}

	@Override
	/**
	 * Obeys the general contract of read. Returns -1 at end of stream.
	 */
	public int read() throws IOException {
		if (index != -1) {
			int len = delimeter.length;
			int i = in.read();
			// record each byte we see
			seen[index] = (byte) i;
			index = (index + 1) % len;

			// see if the last len bytes match the delimeter
			for (int j = 0; j < len; j++) {
				if (delimeter[j] != seen[(index + j) % len]) {
					return i;
				}
			}
			index = -1;
			return i;
		}
		return -1;
	}

	@Override
	/**
	 * Reads up to the delimeter or EOS. In other words, consumes all the bytes
	 * up to and including the delimeter. Does not close the underlying stream.
	 */
	public void close() throws IOException {
		while (read() != -1)
			;
	}
}